package cn.suimg.bilibili;

import cn.suimg.bilibili.api.BarrageConsumer;
import cn.suimg.bilibili.bean.Option;
import cn.suimg.bilibili.bean.Package;
import cn.suimg.bilibili.service.DefaultBarrageConsumerImpl;
import cn.suimg.bilibili.util.HexUtil;
import com.alibaba.fastjson.JSONObject;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.Arrays;

public class SocketClient extends WebSocketClient {

    /**
     * Consumer
     */
    private BarrageConsumer consumer;

    /**
     * Logger
     */
    private static final Logger logger = LoggerFactory.getLogger(SocketClient.class);


    /**
     *
     * @param url
     * @throws URISyntaxException
     */
    public SocketClient(String url) throws URISyntaxException {
        super(new URI(url));
        super.setConnectionLostTimeout(60);
    }

    /**
     * 设置自己的消费端实现
     * @param consumer
     */
    public void setConsumer(BarrageConsumer consumer){
        this.consumer = consumer;
    }

    @Override
    public void connect() {
        super.connect();
        if(consumer == null){
            logger.info("The consumer is not set, using the default implementation");
            consumer = new DefaultBarrageConsumerImpl();
        }
    }

    public void onOpen(ServerHandshake shake) {
        shake.iterateHttpFields().forEachRemaining(e -> {
            logger.debug("open:{}",e);
        });
    }

    /**
     * 发送数据包
     * @param option
     * @param body
     */
    public void send(Option option,String body){
        try {
            byte[] data = new Package(option,body).getBytes();
            logger.debug("send data:{}", HexUtil.bytesToHexString(data));
            super.send(data);
        } catch (IOException e) {
           logger.error("send package error:",e);
        }
    }

    /**
     * 发送空的请求体
     * @param option
     */
    public void send(Option option){
        send(option,"{}");
    }

    @Override
    public void onMessage(ByteBuffer buffer) {
        handleMessage(buffer.array());
    }

    @Override
    public void onMessage(String message) {
        logger.debug("getMessage:{}",message);
    }

    public void onClose(int code, String reason, boolean remote) {
        logger.debug("socket close:{},{},{}",code,reason,remote);
    }

    public void onError(Exception ex) {
        logger.error("socket error",ex);
    }


    public void handleMessage(byte[] array){
        int totalLength = parseHeader(array,0,4);
        //如果弹幕比较多，可能一次接收多个包，所以这里递归去处理 LIFO
        if(totalLength != array.length){
            handleMessage(Arrays.copyOfRange(array,totalLength,array.length));
        }
        int option = parseHeader(array,8,12);
        switch (Option.parseOption(option)){
            case HEARTBEAT_REPLY:
                logger.debug("hearBeat ok");
                break;
            case SEND_MSG_REPLY:
                String rawJson = new String(Arrays.copyOfRange(array, 16, totalLength));
                logger.debug("rawJson:{}",rawJson);
                if(!"".equals(rawJson)){
                    JSONObject json = JSONObject.parseObject(rawJson);

                    switch (json.getString("cmd")){
                        case "NOTICE_MSG":
                            consumer.onNoticeMsg(json);
                            break;
                        case "SYS_MSG":
                            consumer.onSysMsg(json);
                            break;
                        case "WELCOME":
                            consumer.onWelcome(json);
                            break;
                        case "DANMU_MSG":
                            consumer.onDanmuMsg(json);
                            break;
                        case "ROOM_REAL_TIME_MESSAGE_UPDATE":
                          consumer.onReltimeUpdate(json);
                            break;
                        case "SEND_GIFT":
                            consumer.onSendGift(json);
                            break;
                        case "ROOM_RANK":
                           consumer.onRoomRank(json);
                            break;
                        default:
                            logger.info("未处理消息:",json.toJSONString());
                            break;
                    }
                }
                break;
            case ERROR:
                logger.error("unknow option");
                break;
        }
    }

    public static Integer parseHeader(byte[] bytes,int from,int to){
       return HexUtil.byteArrayToInt(Arrays.copyOfRange(bytes,from,to));
    }



}
